package cc.xuzhi.muggle.devtools.service;

import cc.xuzhi.muggle.devtools.mapper.TableMapper;
import cc.xuzhi.muggle.devtools.model.ColumnInfo;
import cc.xuzhi.muggle.devtools.model.GeneratorConfig;
import cc.xuzhi.muggle.devtools.model.TableInfo;
import cc.xuzhi.muggle.devtools.util.CodeGenUtils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;

@Service
public class GeneratorService {

    @Autowired
    private TableMapper tableMapper;

    /**
     * 获取数据库table列表
     * @return
     */
    public List<TableInfo> findTableList() {
        return this.tableMapper.findTableList();
    }

    /**
     * 获取字段列表
     * @param tableName
     * @return
     */
    public List<ColumnInfo> findColumnList(String tableName) {
        return this.tableMapper.findColumnList(tableName);
    }


    /**
     * 代码生成
     * @param config
     */
    public Object generator(GeneratorConfig config) throws IOException {

        TableInfo tableInfo = this.handleTableInfo(config);

        List<ColumnInfo> columnInfos = this.handleColumnInfo(config, tableInfo);

        tableInfo.setColumnInfos(columnInfos);

        // 实体转map
        String json = JSON.toJSONString(tableInfo);
        Map map = JSON.parseObject(json, Map.class);

        // 设置velocity资源加载器
        Properties prop = new Properties();
        prop.put("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        Velocity.init(prop);

        // 设置填入资源值
        VelocityContext context = new VelocityContext(map);

        // 将packageName转为文件存储路径
        String packagePath = config.getPackageName().replaceAll("\\.", "/");

        List<String> templates = getTemplates();
        templates.forEach(d -> {
            //渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(String.format("template/generator/%s", d), "UTF-8");
            tpl.merge(context, sw);

            File file = new File("");
            String format = null;
            try {
                switch (d) {
                    case "Entity.java.vm":
                        format = String.format("%s/%s/src/main/java/%s/model/%s.java",
                                file.getCanonicalPath(),
                                config.getModuleName(),
                                packagePath,
                                tableInfo.getClassName());
                        break;
                    case "Mapper.java.vm":
                        format = String.format("%s/%s/src/main/java/%s/mapper/%sMapper.java",
                                file.getCanonicalPath(),
                                config.getModuleName(),
                                packagePath,
                                tableInfo.getClassName());
                        break;
                    case "Mapper.xml.vm":
                        format = String.format("%s/%s/src/main/resources/mapper/%sMapper.xml",
                                file.getCanonicalPath(),
                                config.getModuleName(),
                                tableInfo.getClassName());
                        break;
                    case "Service.java.vm":
                        format = String.format("%s/%s/src/main/java/%s/service/%sService.java",
                                file.getCanonicalPath(),
                                config.getModuleName(),
                                packagePath,
                                tableInfo.getClassName());
                        break;
                    case "Controller.java.vm":
                        format = String.format("%s/%s/src/main/java/%s/controller/%sController.java",
                                file.getCanonicalPath(),
                                config.getModuleName(),
                                packagePath,
                                tableInfo.getClassName());
                        break;
                }

            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            FileUtil.writeString(sw.toString(), new File(format), "UTF-8");
        });

        return "SUCCESS";
    }

    /**
     * 处理表信息
     * @param config
     * @return
     */
    private TableInfo handleTableInfo(GeneratorConfig config) {
        // 获取表格基础信息
        TableInfo tableInfo = this.tableMapper.findTableByName(config.getTableName());

        // 处理className
        String className = CodeGenUtils.columnToJava(tableInfo.getTableName());
        tableInfo.setClassName(className);
        tableInfo.setClassFiledName(CodeGenUtils.columnToJavaField(tableInfo.getTableName()));

        // 填充基础信息
        tableInfo.setPackageName(config.getPackageName());
        tableInfo.setAuthor(config.getAuthor());
        tableInfo.setDateTime(DateUtil.formatDateTime(new Date()));
        tableInfo.setEnableSwagger(config.isEnableSwagger());

        if(StringUtils.isNotBlank(config.getCommit())) {
            tableInfo.setTableCommit(config.getCommit());
        }

        return tableInfo;
    }

    /**
     * 处理字段信息
     * @param config
     * @return
     */
    private List<ColumnInfo> handleColumnInfo(GeneratorConfig config, TableInfo tableInfo) {
        // 查询字段列表信息
        List<ColumnInfo> columnList = this.tableMapper.findColumnList(config.getTableName());

        columnList.forEach(d -> {
            d.setLowerAttrName(CodeGenUtils.columnToJavaField(d.getColumnName()));
            d.setAttrType(FIELD_TYPE_MAP.get(d.getDataType()));
            if(d.getAttrType().equals("BigDecimal")) {
                tableInfo.setHasBigDecimal(true);
            }
            if(d.getAttrType().equals("Date")) {
                tableInfo.setHasDate(true);
            }

            if ("PRI".equals(d.getColumnKey())){
                d.setPrimaryKey(true);
            }
        });

        return columnList;
    }

    public static Map<String, String> FIELD_TYPE_MAP = new HashMap<String, String>(){{
       this.put("tinyint", "Integer");
       this.put("smallint", "Integer");
       this.put("mediumint", "Integer");
       this.put("int", "Integer");
       this.put("integer", "Integer");
       this.put("bigint", "Long");
       this.put("float", "Float");
       this.put("double", "Double");
       this.put("decimal", "BigDecimal");
       this.put("bit", "Boolean");
       this.put("char", "String");
       this.put("varchar", "String");
       this.put("tinytext", "String");
       this.put("text", "String");
       this.put("mediumtext", "String");
       this.put("longtext", "String");
       this.put("date", "Date");
       this.put("datetime", "Date");
       this.put("timestamp", "Date");
    }};

    public static List<String> getTemplates() {
        List<String> list = new ArrayList<>();
        list.add("Entity.java.vm");
        list.add("Mapper.java.vm");
        list.add("Mapper.xml.vm");
        list.add("Service.java.vm");
        list.add("Controller.java.vm");
        return list;
    }
}
